from Table import *
from Tools import *
from GetInput import *
from Types import *
import Global
import time
import configparser
import os.path

def recordIncomAction():
    record = inputIncomeRecord(Global.categorys.incomeCat1)
    if record == None:
        return
    Global.db.addIncomeRecord(record)
    Global.aofManager.addIncomeRecord(record)

def recordPaymentAction():
    record = inputPaymentRecord(Global.categorys.paymentCat1)
    if record == None:
        return
    Global.db.addPaymentRecord(record)
    Global.aofManager.addPaymentRecord(record)

def recordTransferAction():
    record = inputTransferRecord(Global.categorys.transferCat1)
    if record == None:
        return
    Global.db.addTransferRecord(record)
    Global.aofManager.addTransferRecord(record)

def popertyOverviewsAction():
    data = Global.db.getAccountBalanceData()
    table = Table(['ACCOUNT', 'BALANCE'])
    total = 0
    for row in data:
        table.addRow([row[0], fenToYuanStr(row[1])])
        total += row[1]
    table.addRow(['TOTAL', fenToYuanStr(total)])
    table.show()

def IPOverviewsByDayAction():
    message = '(1) SHOW LAST 15 DAYS\n(2) SHOW IN RANGE\nCHOOSE AN OPTION: '
    ret = inputValidNumberRange(message, 1, 2)
    if ret == 1:
        t = int(time.time())
        endTime = cutTimestampByDay(t)
        beginTime = endTime - 3600*24*15
        showIncomePaymentOverInRange(beginTime, endTime, TimeLength.Day)
    else:
        beginTime = inputValidDatetimeStr('INPUT BEGIN TIME:')
        beginTime = cutTimestampByDay(beginTime)
        endTime = inputValidDatetimeStr('INPUT ENDT TIME:')
        endTime = cutTimestampByDay(endTime)
        showIncomePaymentOverInRange(beginTime, endTime, TimeLength.Day)

def IPOverviewsByMonthAction():
    message = '(1) SHOW LAST 12 MONTH\n(2) SHOW IN RANGE\nCHOOSE AN OPTION: '
    ret = inputValidNumberRange(message, 1, 2)
    if ret == 1:
        currentTime = time.time()
        ts = time.localtime(currentTime)
        endTime = cutTimestampByMonth(currentTime)
        beginTime = int(time.mktime(time.strptime('%s-%s'%(ts.tm_year-1, ts.tm_mon), '%Y-%m')))
        showIncomePaymentOverInRange(beginTime, endTime, TimeLength.Month)
    else:
        beginTime = inputValidDatetimeStr('INPUT BEGIN TIME:')
        beginTime = cutTimestampByMonth(beginTime)
        endTime = inputValidDatetimeStr('INPUT ENDT TIME:')
        endTime = cutTimestampByMonth(endTime)
        showIncomePaymentOverInRange(beginTime, endTime, TimeLength.Month)

def IPOverviewsByYearAction():
    message = '(1) SHOW LAST 10 YEAR\n(2) SHOW IN RANGE\nCHOOSE AN OPTION: '
    ret = inputValidNumberRange(message, 1, 2)
    if ret == 1:
        currentTime = time.time()
        ts = time.localtime(currentTime)
        endTime = cutTimestampByYear(currentTime)
        beginTime = int(time.mktime(time.strptime('%s'%(ts.tm_year - 10), '%Y')))
        showIncomePaymentOverInRange(beginTime, endTime, TimeLength.Year)
    else:
        beginTime = inputValidDatetimeStr('INPUT BEGIN TIME:')
        beginTime = cutTimestampByYear(beginTime)
        endTime = inputValidDatetimeStr('INPUT ENDT TIME:')
        endTime = cutTimestampByYear(endTime)
        showIncomePaymentOverInRange(beginTime, endTime, TimeLength.Year)

def showIncomePaymentOverInRange(beginTime, endTime, timeLength):
    '''展示事件範圍內的收支情況，開始時間和結束時間必須和時間長度所匹配'''
    if timeLength == TimeLength.Day:
        ret = Global.db.getDailyStatisticRecordInRange(beginTime, endTime)
    elif timeLength == TimeLength.Month:
        ret = Global.db.getMonthlyStatisticRecordInRange(beginTime, endTime)
    elif timeLength == TimeLength.Year:
        ret = Global.db.getAnnuallyStatisticRecordInRange(beginTime, endTime)
    else:
        raise ValueError()
    table = Table(['DATE', 'INCOME', 'PAYMENT'])
    for i in ret:
        dateStr = time.strftime('%Y-%m-%d', time.localtime(i[0]))
        table.addRow([dateStr, fenToYuanStr(i[1]), fenToYuanStr(i[2])])
    table.show()

def generateNewBudgetConfigFileAction():
    message = 'THIS OPERATION CLOUD ERASE OLD BUDGET CONFIG FILE.\n\
ARE YOU SURE TO CONINUE?\nINPUT \'YES\' TO CONTINUE, OTHER TO CANCEL\n'
    inStr = input(message)
    if inStr != 'YES':
        print('[CANCELED]')
        return
    budgetConfigFilePath = Global.rootDir + Global.BUDGET_CONFIG_FILE_PATH
    cat1List = []
    for cat1 in Global.categorys.paymentCat1[1:]:
        item = (cat1.id, cat1.name, cat1.showable)
        cat1List.append(item)
    generateEmptyBudgetConfigFile(cat1List, budgetConfigFilePath)
    print('THE BUDGET CONFIG FILE IS AT: %s'%(budgetConfigFilePath))

def checkBudgetConfigValidity(budgetList):
    '''檢查預算配置的有效性，有效返回0，否則返回錯誤字符串
    budgetList: 結構為[[cat1Id, [m1, m2, ... , m12, year]], ...]'''
    totalMonthAndYear = None # 配置文件中的總預算
    minTotalMonthAndYear = [0,0,0,0,0,0,0,0,0,0,0,0,0]
    for budgetItem in budgetList:
        cat1Id = budgetItem[0]
        monthAndYear = budgetItem[1]
        if cat1Id == 0:
            totalMonthAndYear = monthAndYear
            continue
        if len(monthAndYear) != 13:
            return '[ERROR] LENGTH OF ITEM [%d] IS ILLEGAL!'%(cat1Id)
        allMonthBudget = 0
        for i in range(0, 12):
            if type(monthAndYear[i]) != int:
                return '[ERROR] BUDGET SHOULD BE NUMBER OF ITEM [%d]!'%(cat1Id)
            allMonthBudget += monthAndYear[i]
            minTotalMonthAndYear[i] += monthAndYear[i]
        if allMonthBudget > monthAndYear[12]:
            return '[ERROR] YEAR BUDGET SHOULD EQUAL OR LARGET THAN ALL MONTH BUDGET OF ITEM [%d]!'%(cat1Id)
        minTotalMonthAndYear[12] += monthAndYear[12]
    for i in range(0, 13):
        if minTotalMonthAndYear[i] > totalMonthAndYear[i]:
            return '[ERROR] TOTAL YEAR BUDGET SHOULD EQUAL OR LARGET THAN SUM OF ALL MONTH BUDGET! [%d]'%(i)
    return 0    


def loadBudgetConfigFileAction():
    #读取预算配置文件
    #暂存在一个列表里，其结构为：[[cat1Id, [m1, m2, ... , m12, year]], ...]
    budgetList = []
    budgetConfFilePath = Global.rootDir + Global.BUDGET_CONFIG_FILE_PATH
    if os.path.exists(budgetConfFilePath) == False:
        print('BUDGET CONFIG FILE NOT EXIST! FILE NAME: %s'%(budgetConfFilePath))
        return
    conf = configparser.ConfigParser()
    conf.read(budgetConfFilePath, 'utf-8')
    cat1IdsInConf = conf.sections()
    # 判斷配置文件中出現的CAT1_ID是否有效（存在且未被刪除）
    cat1IdsInDB = set()
    for cat1Id in range(1, len(Global.categorys.paymentCat1)):
        if Global.categorys.paymentCat1[cat1Id].showable == 1:
            cat1IdsInDB.add(cat1Id)
    cat1IdsInDB.add(0) # 代表總計的特殊值
    for cat1Id in cat1IdsInConf:
        try:
            cat1Id = int(cat1Id)
        except:
            print('[ERROR] CAT1_ID SHOULD BE NUMBER!')
            return 
        if cat1Id not in cat1IdsInDB:
            print('[ERROR] CAT1_ID %d IS NOT EXIST! PLEASE CHECK YOUR BUDGET CONFIG FILE!'%(cat1Id))
            return 
    # 讀取配置文件中的內容
    for cat1Id in cat1IdsInConf:
        item = conf.get(cat1Id, 'MONTH', fallback=None)
        if item == None:
            print('[ERROR]: CONTENT OF "MONTH" IS NOT EXIST IN CATEGORY [%s]'%(cat1Id))
            return
        budgetItemList = item.split(',')
        if len(budgetItemList) != 12:
            print('[ERROR]: 1 CONTENT OF "MONTH" IS NOT APPROPRIATE IN CATEGORY [%s]'%(cat1Id))
            return
        for i in range(0, len(budgetItemList)):
            try:
                budgetItemList[i] = int(budgetItemList[i]) * 100
            except Exception as e:
                print('[ERROR]: 2 CONTENT OF "MONTH" IS NOT APPROPRIATE IN CATEGORY [%s]'%(cat1Id), e)
                return
        item = conf.get(cat1Id, 'YEAR', fallback=None)
        if item == None:
            print('[ERROR]: CONTENT OF "YEAR" IS NOT EXIST IN CATEGORY [%s]'%(cat1Id))
            return
        try:
            yearBudget = int(item) * 100
        except:
            print('[ERROR]: CONTENT OF "YEAR" IS NOT APPROPRIATE IN CATEGORY [%s]'%(cat1Id))
            return
        budgetItemList.append(yearBudget)
        budgetList.append([int(cat1Id), budgetItemList])
    # 檢查配置中的數據的合理性
    ret = checkBudgetConfigValidity(budgetList)
    if ret != 0:
        print(ret)
        return
    # 写入数据库
    Global.db.setBudget(budgetList)
    Global.aofManager.setBudget(budgetList)
    print('[DONE]')
    
def showBudgetPlanAction():
    budgetPlan = Global.db.getBudgetPlanWithoutDeleteCat1()
    tableTitles = ['NAME','JAN','FEB','MAR','APR','MAY','JUN','JLY','AUG','SEP','OCT','NOV','DEC','YEAR']
    table = Table(tableTitles)
    for plan in budgetPlan[1:]:
        row = []
        row.append(plan[0])
        for i in range(2, 2 + 12 + 1): #一月的預算從下標4開始，12個月，還有全年預算
            row.append(fenToYuanStr(plan[i]))
        table.addRow(row)
    # 單獨處理總預算
    table.addEmptyRow()
    plan = budgetPlan[0]
    row = []
    row.append(plan[0])
    for i in range(2, 2 + 12 + 1): #一月的預算從下標4開始，12個月，還有全年預算
        row.append(fenToYuanStr(plan[i]))
    table.addRow(row)
    table.show()

def showBudgetUsageAction():
    currentTm = time.localtime()
    currentMonthNumber = currentTm.tm_mon
    currentMonthTm = time.strptime('%d-%d'%(currentTm.tm_year, currentTm.tm_mon), '%Y-%m')
    currentMonth = time.mktime(currentMonthTm)
    currentYearTm = time.strptime(str(currentTm.tm_year), '%Y')
    currentYear = time.mktime(currentYearTm)
    currentTime = time.mktime(currentTm)
    paymentInThisMonth = Global.db.getPaymentCat1AddupInRange(currentMonth, currentTime)
    paymentInThisYear = Global.db.getPaymentCat1AddupInRange(currentYear, currentTime)
    totalPyamentInThisMonth = Global.db.getPaymentTotalInRange(currentMonth, currentTime)
    totalPaymentInThisYear = Global.db.getPaymentTotalInRange(currentYear, currentTime)###################################
    budgetPlan = Global.db.getBudgetPlanWithoutDeleteCat1()
    budgetPlan = splitBudgetPlan(budgetPlan, currentMonthNumber)
    tableTitle = ['NAME','MONTH', 'USAGE', 'FREE', 'ADDUP FREE', 'YEAR', 'USAGE', 'FREE']
    table = Table(tableTitle)
    for plan in budgetPlan[1:]:
        cat1Name = plan[1]
        cat1Id = plan[0]
        monthBudget = plan[2]
        addupBudget = plan[3]
        yearBudget = plan[4]
        if cat1Id in paymentInThisMonth:
            monthUsage = paymentInThisMonth[cat1Id]
            if monthBudget == 0:
                monthUsagePercent = 0.0
            else:
                monthUsagePercent = paymentInThisMonth[cat1Id] / monthBudget * 100
        else:
            monthUsage = 0
            monthUsagePercent = 0.0
        if cat1Id in paymentInThisYear:
            yearUsage = paymentInThisYear[cat1Id]
            if yearBudget == 0:
                yearUsagePercent = 0.0
            else:
                yearUsagePercent = paymentInThisYear[cat1Id] / yearBudget * 100
        else:
            yearUsage = 0
            yearUsagePercent = 0.0
        monthFree = monthBudget - monthUsage
        addupFree = addupBudget - yearUsage
        yearFree = yearBudget - yearUsage
        row = [cat1Name, fenToYuanStr(monthBudget), '%.2f %%'%(monthUsagePercent), fenToYuanStr(monthFree), \
            fenToYuanStr(addupFree), fenToYuanStr(yearBudget), '%.2f %%'%(yearUsagePercent), fenToYuanStr(yearFree)]
        table.addRow(row)
    # 單獨處理"總計"
    table.addEmptyRow()
    plan = budgetPlan[0]
    cat1Name = plan[1]
    monthBudget = plan[2]
    addupBudget = plan[3]
    yearBudget = plan[4]
    monthUsage = totalPyamentInThisMonth
    if monthBudget == 0:
        monthUsagePercent = 0.0
    else:
        monthUsagePercent = totalPyamentInThisMonth / monthBudget * 100
    yearUsage = totalPaymentInThisYear
    if yearBudget == 0:
        yearUsagePercent = 0.0
    else:
        yearUsagePercent = totalPaymentInThisYear / yearBudget * 100
    monthFree = monthBudget - monthUsage
    addupFree = addupBudget - yearUsage
    yearFree = yearBudget - yearUsage
    row = [cat1Name, fenToYuanStr(monthBudget), '%.2f %%'%(monthUsagePercent), fenToYuanStr(monthFree), \
            fenToYuanStr(addupFree), fenToYuanStr(yearBudget), '%.2f %%'%(yearUsagePercent), fenToYuanStr(yearFree)]
    table.addRow(row)
    table.show()

def deleteRecord(recordId, recordType):
    print('id:',recordId,'type',recordType)
    if recordType == RecordType.Income:
        record = Global.db.getIncomeRecord(recordId)
    if recordType == RecordType.Payment:
        record = Global.db.getPaymentRecord(recordId)
    if recordType == RecordType.Transfer:
        record = Global.db.getTransferRecord(recordId)
    print(record,'\n')
    inStr = input('ARE YOU SURE TO DELETE THIS RECORD?\nINPUT \'YES\' TO CONFIRM, OTHERS TO CANCEL: ')
    if inStr != 'YES':
        print('[CANCELED]')
        return
    if recordType == RecordType.Income:
        Global.db.deleteIncomeRecord(recordId)
        Global.aofManager.deleteIncomeRecord(recordId)
    if recordType == RecordType.Payment:
        Global.db.deletePaymentRecord(recordId)
        Global.aofManager.deletePaymentRecord(recordId)
    if recordType == RecordType.Transfer:
        Global.db.deleteTransferRecord(recordId)
        Global.aofManager.deleteTransferRecord(recordId)
    print('[DELETED]')

def recordsToTable(records, table):
    '''把records的内容写入table中
    records: Global.db.getRecordInRange的返回结果
    table: 要写入的表格，会覆盖表格原有的内容
    返回idIndex列表，第0个元素不用'''
    table.clearAllRows()
    idIndex = [None]
    row = []
    i = 1
    for record in records:
        idIndex.append(record[0])
        row.clear()
        row.append(str(i))                          # index
        row.append(timestampToString(record[1]))    # time
        cat1Type = record[2]
        row.append(cat1TypeIntToString(cat1Type))   # type
        row.append(Global.accounts[record[3]].name) # account
        if cat1Type == RecordType.Income:           # category
            row.append(Global.categorys.incomeCat1[record[4]].name)
        elif cat1Type == RecordType.Payment:
            cat1Id = Global.categorys.paymentCat2[record[4]].cat1Id
            row.append(Global.categorys.paymentCat1[cat1Id].name)
        elif cat1Type == RecordType.Transfer:
            row.append(Global.categorys.transferCat1[record[4]].name)
        else:
            raise ValueError()
        row.append(fenToYuanStr(record[5]))           # amount
        row.append(record[6])                         # description
        table.addRow(row)
        i += 1
    return idIndex

def showTableInPage(tableTitleList, dataList, maxRowInPage, **rowActionHandler):
    '''分頁顯示表格
    交互方法：'0'：退出，'['：上一頁，']'：下一頁，表格索引：調用處理函數，回車：下一頁
    tableTitleList: 表格的標題
    dataList: 要顯示的所有數據，類型為列表
    maxRowInPage: 每頁最多顯示的行數
    rowActionHandler: 用戶選中某一行對應的處理函數，參數為該行數據在dataList中的下標
    '''
    pass
    
def showRecordInRange(beginTime, endTime):
    '''展示指定時間範圍內的所有記錄
    每次最多展示MAX_SHOW_RECORD_COUNT條記錄'''
    if endTime < beginTime:
        raise ValueError(" endTime should > beginTime")
    recordCount = Global.db.getRecordInRangeCount(beginTime, endTime)
    if recordCount == 0:
        print('EMPTY!\n')
        return 0
    idIndex = [None] 
    titles = ("INDEX", "TIME", "TYPE", "ACCOUNT", "CATEGORY", "AMOUNT", "DESCRIPTION")
    table = Table(titles)
    showedCount = 0 # 已经展示过的个数
    while True:
        if showedCount >= recordCount:
            print('NO MORE RECORD! EXIT.')
            break
        records = Global.db.getRecordInRange(beginTime, endTime, showedCount, Global.MAX_SHOW_RECORD_COUNT)
        showCount = len(records) # 本次要展示的个数
        idIndex = recordsToTable(records, table)
        table.show()
        message = 'PRESS ENTER TO CONTINUE/EXIT\nINPUT INDEX WHICH YOU WANT TO DELETE\nINPUT 0 TO EXIT: '
        index = inputValidNumberRangeOrEmpty(message, 0, showCount)
        print()
        if index == None:
            showedCount += showCount
            continue
        if index == 0:
            return
        recordId = idIndex[index]           # idIndex的有效元素從第1個開始
        recordType = records[index-1][2]    # records的有效元素從第0個開始，所以下標要減1
        deleteRecord(recordId, recordType)
        return

def showAllRecordAction():
    showRecordInRange(0, int(time.time()))
    
def showWeekRecordAction():
    ts = time.localtime(time.time())
    timeStr = "%d-%d-%d"%(ts.tm_year, ts.tm_mon, ts.tm_mday)
    begin = timestampFromString(timeStr + " 00:00")
    begin = begin - ts.tm_wday * 24 * 3600
    end = int(time.time())
    showRecordInRange(begin, end)

def showMonthRecordAction():
    ts = time.localtime(time.time())
    timeStr = "%d-%d-01"%(ts.tm_year, ts.tm_mon)
    begin = timestampFromString(timeStr + " 00:00")
    end = int(time.time())
    showRecordInRange(begin, end)

def showRangeRecordAction():
    beginTime = inputValidDatetimeStr("Begin time:")
    endTime = inputValidDatetimeStr("End time:")
    showRecordInRange(beginTime, endTime)

def showAndGetIncomeCategory():
    '''顯示未被刪除的收入分類，并返回顯示的INDEX與ID的對應關係的列表
    列表的下標與顯示的INDEX相同，對應的值為ID
    第0個元素不使用'''
    ret = [None]
    table = Table(['INDEX', 'NAME'])
    index = 1
    for i in range(1, len(Global.categorys.incomeCat1)):
        if Global.categorys.incomeCat1[i].showable == True:
            table.addRow([str(index), Global.categorys.incomeCat1[i].name])
            ret.append(i)
            index += 1
    table.show()
    return ret

def showIncomeCategoryAction():
    showAndGetIncomeCategory()
    
def addIncomeCategoryAction():
    pass

def deleteIncomeCategoryAction():
    showAndGetIncomeCategory()
    message = 'SELECT CATEGORY ID WHICH YOU WANT TO DELETE'

def editIncomeCategoryAction():
    showAndGetIncomeCategory()

# showPaymentCategoryAction
# addPaymentCategoryAction
# deletePaymentCategoryAction 
# editPaymentCategoryAction

def showEditDeletePaymentCategories():
    pass

def dbmAction():
    while True:
        dbStr = input("Input SQL statement, input \"exit/EXIT\" to exit:\n")
        if dbStr == "exit" or dbStr == 'EXIT':
            return
        try:
            ret = Global.db.executeSQL(dbStr)
            print('[OK]')
            for i in ret:
                print(i)
        except Exception as e:
            print('[ERROR]  ', e)

def rebuildAllDataAction():
    '''清空數據庫中的數據，然後從AOF中導入數據'''
    message = 'ARE YOUR SURE TO REBUILD ALL DATA?\nINPUT \'YES\' TO CONTINUE: '
    inStr = input(message)
    if inStr != 'YES':
        return
    print('CLEAR ALL DATA ...')
    Global.db.deleteAllData()
    print('REBUILD DATA ...')
    Global.aofManager.readAOF()
    print('DONE!\n')

def aboutAction():
    print()
    print('VERSION     : %s'%(Global.VERSION_STRING))
    print('AUTHOR      : Three Lee')
    print('LICENSE     : GPLv3')
